﻿using System.Diagnostics;

namespace DimensionsHelper.LanguageService.Syntax;

public class SyntaxNodeVisitor<T>(
    Predicate<T> predicateNode,
    Predicate<SyntaxList<T>>? predicateList,
    Predicate<SyntaxComment>? predicateComment)
    where T : class, ITextNode
{
    private SyntaxNode? _node;
    private SyntaxToken? _token;
    private SyntaxComment? _trivia;


    internal bool IsFinished { get; private set; }


    public bool Predicate(T node)
    {
        return predicateNode(node);
    }

    public bool Predicate(SyntaxList<T> nodeList)
    {
        return predicateList?.Invoke(nodeList) ?? false;
    }

    public bool Predicate(SyntaxComment trivia)
    {
        return predicateComment?.Invoke(trivia) ?? false;
    }


    public SyntaxComment? GetTrivia()
    {
        return _trivia;
    }


    public SyntaxToken? GetToken()
    {
        return _token;
    }


    public SyntaxNode? GetNode()
    {
        return _node;
    }


    public void Finish(SyntaxNode node)
    {
        Debug.Assert(!IsFinished);
        IsFinished = true;
        _node = node;
    }


    public void Finish(SyntaxToken token)
    {
        Debug.Assert(!IsFinished);
        IsFinished = true;
        _token = token;
    }


    public void Finish(SyntaxComment trivia)
    {
        Debug.Assert(!IsFinished);
        IsFinished = true;
        _trivia = trivia;
    }


    public void VisitComment(ITextNode node)
    {
        if (IsFinished)
        {
            return;
        }

        foreach (SyntaxComment? comment in node.GetLeadingComments())
        {
            if (predicateComment?.Invoke(comment) != true)
            {
                continue;
            }

            Finish(comment);
            return;
        }
    }
}


public class SyntaxNodeVisitor(
    Predicate<ITextNode> nodePredicate,
    Predicate<SyntaxList<ITextNode>>? listPredicate,
    Predicate<SyntaxComment>? predicateTrivia)
    : SyntaxNodeVisitor<ITextNode>(nodePredicate, listPredicate, predicateTrivia)
{
    public static SyntaxNodeVisitor GetPositionVisitorInstance(int position)
    {
        return new SyntaxNodeVisitor(
            node => node.Span.Contains(position),
            list => list.Span.Contains(position),
            trivia => trivia.Span.Contains(position));
    }


    public static SyntaxNodeVisitor GetPositionVisitorInstance(int position, Predicate<ITextNode> predicate,
        Predicate<SyntaxList<ITextNode>>? listPredicate)
    {
        return new SyntaxNodeVisitor(
            node => node.Span.Contains(position) && predicate(node),
            list => list.Span.Contains(position) && (listPredicate == null || listPredicate(list)),
            trivia => trivia.Span.Contains(position));
    }
}